#ifndef RESTAURANTE
#define RESTAURANTE

#include <iostream>
#include <string>
#include "DiccTrie.h"
#include "ConjString.h"
#include "Multiconjunto.h"
#include "Recetario.h"
#include <math.h>

using namespace std;

struct InfoMesa {
	string mozo;
	bool libre;
	MConj pedidos;

};

int toInt(string str) { 
		int retorno = 0;
		int i = 0;
		int largo = str.length();
		while (i != largo) {
			retorno += (str[largo - i - 1] - 97) * (pow(10, i)); 
			i++;
			} 
		return retorno;
	}

class Restaurante {

public:
	~Restaurante();
	Restaurante(int cantMesas, const ConjString &mozos, const DiccTrie<ConjString> &asignacion, const Recetario &recetario);
	bool ocuparMesa(int mesa);
	bool hacerPedido(int mesa, const MConj &pedido);
	void pagar(const int mesa);
	bool prepararPlato(const int mesa, const string &plato);
	void comprarIngrediente(const string &ingrediente, int cantidad);
	int cantMesas() const;
	const ConjString & verMozos() const;
	const Recetario & verRecetario() const;
	int dameStock(const string &ingrediente) const;
	const MConj &pedidosPendientes(int mesa) const;
	const string &mozoAsignado(int mesa) const;
	const ConjString &mesasDelMozo(const string &mozo) const;
	bool mesaOcupada(int mesa) const;
	int cantMesasMozo(const string &mozo) const;
	bool sePuedePreparar(const MConj &pedido) const;
	
	
	
private:
	
	/* si bien no estaba en el diseño se lo agregamos para obtener la cantidad de Mesas en orden 1 ya que el c++ no permite saber el tamaño de un arreglo
	   de tamaño brindado dinámicamente en O(1)
	*/
	int cantM;
	InfoMesa ** mesas;
	DiccTrie<ConjString> asignacion;
	MConj stock; // esto estaba mal en el diseño, de otro modo había que setear el diccionario en 0 para todos los ingredientes del recetario
	Recetario recetario;	
	
	
};

Restaurante::Restaurante(int cantMesas, const ConjString &mozos, const DiccTrie<ConjString> &asig, const Recetario &rec) {
	string moz;

	IteradorConjString it2;
	int mes;
	mesas = new InfoMesa*[cantMesas];
	int i = 0;

	while ( i < cantMesas ) {
		mesas[i] = new InfoMesa;
		mesas[i] -> libre = true;	
		i++;
	}
	IteradorConjString it(mozos);

	while (it.tieneProximo()) {
		moz = it.proximo();

		it2 = IteradorConjString(asig.obtener(moz));
		while( it2.tieneProximo()) {
			mes = toInt(it2.proximo());

			((mesas[mes]) -> mozo) = moz;
			((mesas[mes]) -> pedidos)= MConj();

	
		}
	}
	asignacion = asig;
	recetario = rec;
	cantM = cantMesas;

	

}
Restaurante::~Restaurante() {
	int cantM = cantMesas();
	int i = 0;
	while ( i < cantM ) {
		delete mesas[i];
	
		i++;
	}
	delete [] mesas;


}
bool Restaurante::ocuparMesa(int mesa) {
	
	if ( (this -> mesaOcupada(mesa)) && mesa < this->cantMesas() ) {
		(mesas)[mesa] -> libre = false;
		return true;
	}
	else {
	 	return false;
	}
}

bool Restaurante::hacerPedido(int mesa, const MConj &pedido) {
	
	bool ret = false;
	int stockRequerido;


	string ingTemp;

	if ( !(this -> mesaOcupada(mesa)) && mesa < this->cantMesas() ) {

			
		if ( pedido.mTieneTodos(recetario.platos()) ) {

			if ( this->sePuedePreparar(pedido) ) {
				( (mesas)[mesa] -> pedidos ).mUnion(pedido);
				ret = true;

				ConjString ingredientes(recetario.ingredientesNecesariosPedido(pedido));
				IteradorConjString iterIng(ingredientes);
				cout << iterIng.proximo();
				while( iterIng.tieneProximo() ) {
					ingTemp = iterIng.proximo();
					stockRequerido = recetario.cantidadNecesariaPedido(pedido, ingTemp);
					stock.mQuitarN(ingTemp, stockRequerido);
				}

			}
		}
	
	}
	return ret;

}

void Restaurante::pagar(const int mesa) {
// en la precondición está que tiene que no tener platos pendientes
	(mesas)[mesa] -> libre = true;
}

bool Restaurante::prepararPlato(const int mesa, const string &plato) {
	
	bool ret = false;
	
	if ( 	mesa < this->cantMesas() && !(this -> mesaOcupada(mesa)) && 
		(this->pedidosPendientes(mesa)).mPertenece(plato) ) {
		
		((mesas)[mesa] -> pedidos).mQuitarUno(plato);
		ret = true;
	}
	
	return ret;

}

void Restaurante::comprarIngrediente(const string &ingrediente, int cantidad) {
	
	stock.mAgregarN(ingrediente, cantidad);
	
}	

// Esto no cambia el orden de las funciones relevantes. Otra opción era construir una clase ArregloDinámico que nos devolviera en orden 1 el tamaño.
int Restaurante::cantMesas() const {	// duda, capaz hace cualquiera
	

	return cantM;

}

const ConjString & Restaurante::verMozos() const {
	return asignacion.claves();
}	

const Recetario & Restaurante::verRecetario() const {
	return recetario;
}

int Restaurante::dameStock(const string &ingrediente) const {

	return  stock.mCantidad(ingrediente);;
}

const MConj & Restaurante::pedidosPendientes(int mesa) const {
	return (mesas)[mesa] -> pedidos;
}

const string & Restaurante::mozoAsignado(int mesa) const {
	return (mesas)[mesa] -> mozo;
}

const ConjString & Restaurante::mesasDelMozo(const string &mozo) const {
	return asignacion.obtener(mozo);
}

bool Restaurante::mesaOcupada(int mesa) const {
	return !((mesas)[mesa] -> libre);
}

int Restaurante::cantMesasMozo(const string &mozo) const {
	return (asignacion.obtener(mozo)).dameTamano();
}


bool Restaurante::sePuedePreparar(const MConj &pedido) const {

	bool ret = true;
	MConj ingsPedido = recetario.ingredientesPedido(pedido);
	IteradorMConj it(ingsPedido);
	string ingrediente;

	while ( it.tieneProximo() ) {
		ingrediente = it.proximo();
		if ( stock.mCantidad(ingrediente) <= ingsPedido.mCantidad(ingrediente) )
			ret = false;
	}
	
	return ret;

}

#endif
